﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.OracleClient;
using System.Web;
using System.Text.RegularExpressions;


namespace CCPSIP
{
  public class AppleKit
  {
    private const string kOracleServer = "MIS.CCP.COM.TW";
    private const string kOracleUserId = "AC";
    private static byte[] kOracleKey = { 0x54, 0x35, 0x30, 0x30, 0x31, 0x38, 0x35, 0x35 };

    private const int kIdHeaderLen = 5;
    private const int kCheckcodeLength = 64; //SHA-256
    private const string kTimePasswordFormat = ",yyyyMMdd_HHmm";
    private const int kTimeDeviationRange = 10; //minutes

    public const string kLoginErrorMsg = "ERROR_01: 手機識別失敗";
    public const string kUpdateSuccessMsg = "UpdateOK";
    public const string kOptionNameSeparator = ";_;";

    private const string kUnknownVersionStr = "UNKNOWN_VER";

    private static string GetConnStrMIS() {
      return String.Format(
        "Data Source={0};Persist Security Info=True;User ID={1};Password={2}",
        kOracleServer, kOracleUserId, System.Text.Encoding.UTF8.GetString(kOracleKey));
    }


    /// <summary>
    /// Log iOS App版本存取記錄
    /// </summary>
    /// <param name="appID">iOS App ID</param>
    /// <param name="appVer">App版本</param>
    /// <param name="osVer">iOS版本</param>
    /// <param name="userObj">UserInfo物件</param>
    public static void insertAppLog(string appID, string appVer, System.Web.HttpRequest req, SipUserInfo userObj)
    {
      OracleConnection connMIS = new OracleConnection(GetConnStrMIS());
      try {
        connMIS.Open();
        string osVer = string.Format("{0} {1}", GetiDeviceName(req.UserAgent), GetIOSVersion(req.UserAgent));
        string logStr = "INSERT INTO IOS_APP_LOG(APPID, APPVER, CNAME, UDID, OSVER)"
          + " VALUES(:APPID, :APPVER, :CNAME, :UDID, :OSVER)";
        OracleCommand logCmd = new OracleCommand(logStr, connMIS);
        logCmd.Parameters.Add("APPID", OracleType.VarChar).Value = appID;
        logCmd.Parameters.Add("APPVER", OracleType.VarChar).Value = appVer;
        logCmd.Parameters.Add("CNAME", OracleType.VarChar).Value = userObj.FullName;
        logCmd.Parameters.Add("UDID", OracleType.VarChar).Value = userObj.UUID;
        logCmd.Parameters.Add("OSVER", OracleType.VarChar).Value = osVer;
        logCmd.ExecuteNonQuery();
      }
      catch (Exception ex) {
        Console.WriteLine(ex.Message);
      }
      finally {
        connMIS.Close();
      }
    }

    public static bool insertAndroid_AppLog(String type, String appId, String appVer,
          String userId, String userIp, String user_agent)
    {
        OracleConnection connOracle = new OracleConnection(GetConnStrMIS());
        try
        {
            connOracle.Open();
            string insStr = "INSERT INTO AC.ANDROID_APP_LOG"
                + "(APP_ID, APP_VER, USER_ID, USER_IP, USER_AGENT, LOG_TYPE)"
                + "VALUES (:APP_ID, :APP_VER, :USER_ID, :USER_IP, :USER_AGENT, :LOG_TYPE)";
            OracleCommand insCmdOra = new OracleCommand(insStr, connOracle);
            insCmdOra.Parameters.Add("APP_ID", OracleType.VarChar).Value = appId;
            insCmdOra.Parameters.Add("APP_VER", OracleType.VarChar).Value = appVer;
            insCmdOra.Parameters.Add("USER_ID", OracleType.VarChar).Value = userId;
            insCmdOra.Parameters.Add("USER_IP", OracleType.VarChar).Value = userIp;
            insCmdOra.Parameters.Add("USER_AGENT", OracleType.VarChar).Value = user_agent;
            insCmdOra.Parameters.Add("LOG_TYPE", OracleType.VarChar).Value = type;
            int rowsUpdated = insCmdOra.ExecuteNonQuery();
            if (rowsUpdated == 0)
                return false;
            else
                return true;
        }
        finally
        {
            connOracle.Close();
        }
    }

    /// <summary>
    /// 寫入訊息到APNS Queue待發送。
    /// </summary>
    /// <param name="appID">App ID</param>
    /// <param name="receiver">收訊人</param>
    /// <param name="message">訊息內文</param>
    /// <param name="soundFile">提示聲音檔</param>
    public static void insertAPNsQueue(string appID, string receiver, string message, string soundFile) {
      if (message.Length > 73)
        message = message.Substring(0, 72) + "...";
      OracleConnection connMIS = new OracleConnection(GetConnStrMIS());
      try {
        connMIS.Open();

        string insStr = "Insert into IOS_APNS_QUEUE"
          + " (PID, APPID, RECEIVER, CREATETIME, CREATOR, MESSAGE, SOUND)"
          + " Values (IOS_APNS_QUEUE_PK_SEQ.NEXTVAL, :APPID, :RECEIVER, :CREATETIME, :CREATOR, :MESSAGE, :SOUND)";
        OracleCommand insCmd = new OracleCommand(insStr, connMIS);
        insCmd.Parameters.Add("APPID", OracleType.VarChar).Value = appID;
        insCmd.Parameters.Add("RECEIVER", OracleType.VarChar).Value = receiver;
        insCmd.Parameters.Add("CREATETIME", OracleType.DateTime).Value = DateTime.Now;
        insCmd.Parameters.Add("CREATOR", OracleType.VarChar).Value = "WebService";
        insCmd.Parameters.Add("MESSAGE", OracleType.VarChar).Value = message;
        insCmd.Parameters.Add("SOUND", OracleType.VarChar).Value = soundFile;
        insCmd.ExecuteNonQuery();
      }
      catch (Exception ex) {
        Console.WriteLine(ex.Message);
      }
      finally {
        connMIS.Close();
      }
    }


    /// <summary>
    /// 檢查User是否為測試人員
    /// </summary>
    /// <param name="userObj">UserInfo物件</param>
    /// <returns>測試人員return true, others false.</returns>
    public static bool isTestUser(UserInfo userObj, string appID) {
      if (userObj == null)
        return false;

      OracleConnection connMIS = new OracleConnection(GetConnStrMIS());
      try {
        connMIS.Open();

        string selStr = "SELECT USERNAME FROM IOS_USER_RIGHT"
          + " WHERE APPID=:APPID AND RIGHT_DESC='Test'";
        OracleCommand selCmd = new OracleCommand(selStr, connMIS);
        selCmd.Parameters.Add("APPID", OracleType.VarChar).Value = appID;
        OracleDataAdapter adapter = new OracleDataAdapter(selCmd);
        DataSet dataSet = new DataSet();
        adapter.Fill(dataSet);

        foreach (DataRow row in dataSet.Tables[0].Rows) {
          if (userObj.Username == row["USERNAME"].ToString())
            return true;
        }
      }
      catch (Exception ex) {
        Console.WriteLine("Error:" + ex.Message);
      }
      finally {
        connMIS.Close();
      }
      return false;
    }


    


    /// <summary>
    /// 取得Mobile Device持有人的UserInfo物件
    /// </summary>
    /// <param name="idHeader"></param>
    /// <param name="checkcode"></param>
    /// <param name="request"></param>
    /// <returns>UserInfo物件</returns>
    public static AppleKit.UserInfo GetUserInfo(string idHeader, string checkcode, string appName, HttpRequest req) {
      DateTime requestTime = DateTime.Now;
      string ipAddr = ChrisKit.getIPAddress(req);

      //【01】檢查參數
      //
      if (string.IsNullOrEmpty(idHeader) || string.IsNullOrEmpty(checkcode)) {
        writeLoginErrLog("(null)", "(null)", appName, ipAddr);
        return null;
      }
      if (idHeader.Length != kIdHeaderLen || checkcode.Length != kCheckcodeLength) {
        writeLoginErrLog(idHeader, checkcode, appName, ipAddr);
        return null;
      }
      checkcode = checkcode.ToLower();

      //【02】產生可容忍時間的字串List
      //
      List<string> timeStrList = new List<string>(kTimeDeviationRange * 2 + 1);
      timeStrList.Add(requestTime.ToString(kTimePasswordFormat));
      for (int i = 1; i <= kTimeDeviationRange; i++) {
        timeStrList.Add(requestTime.AddMinutes(i).ToString(kTimePasswordFormat));
        timeStrList.Add(requestTime.AddMinutes(-i).ToString(kTimePasswordFormat));
      }

      //【03】連結DB，篩選符合的User
      //
      OracleConnection connMIS = new OracleConnection(GetConnStrMIS());
      try {
        connMIS.Open();

        //【03-1】篩選出UDID開頭符合的User，比對Hash字串
        //
        string cmdStr = "SELECT O.UDID AS UUID, O.DEVICETYPE AS DEVICE_TYPE,"
          + " U.USERNAME, U.PPS_CODE, U.EMAIL_ADDR, U.WIN_ID"
          + " FROM IOS_OWNER O, IOS_USERS U"
          + " WHERE O.OWNER=U.USERNAME AND O.STATUS='1' AND O.UDID LIKE :UUID";
        OracleCommand dbCmd = new OracleCommand(cmdStr, connMIS);
        dbCmd.Parameters.Add("UUID", OracleType.VarChar).Value = idHeader + "%";
        OracleDataAdapter adapter = new OracleDataAdapter(dbCmd);
        DataSet dataSet = new DataSet();
        adapter.Fill(dataSet);

        foreach (DataRow row in dataSet.Tables[0].Rows) {
          string uuid = row["UUID"].ToString();
          foreach (string timeStr in timeStrList) {
            if (checkcode == HashKit.getHashStr(uuid + timeStr, HashType.SHA256)) {
              UserInfo userObj = new UserInfo(row, req);
              return userObj;
            }
          }
        }

        writeLoginErrLog(idHeader, checkcode, appName, ipAddr);
      }
      catch (Exception ex) {
        ChrisKit.SkipVar(ex);
        writeLoginErrLog(idHeader, checkcode, appName, ipAddr);
      }
      finally {
        connMIS.Close();
      }
      return null;
    }


    /// <summary>
    /// 記錄登入失敗的登入參數
    /// </summary>
    /// <param name="idHeader"></param>
    /// <param name="checkcode"></param>
    public static void writeLoginErrLog(string idHeader, string checkcode, string appName, string ipAddr) {
      OracleConnection connMIS = new OracleConnection(GetConnStrMIS());
      try {
        if (idHeader.Length > 10)
          idHeader = idHeader.Substring(0, 10);
        if (checkcode.Length > 100)
          checkcode = checkcode.Substring(0, 100);

        connMIS.Open();
        string insStr = "Insert Into IOS_ERR_LOG(ERRTYPE, USERNAME, ERRMSG, KEY1, IP_ADDR)"
          + " Values ('Login', :idHeader, :checkcode, :appName, :IP_ADDR)";
        OracleCommand insCmd = new OracleCommand(insStr, connMIS);
        insCmd.Parameters.Add("idHeader", OracleType.VarChar).Value = idHeader;
        insCmd.Parameters.Add("checkcode", OracleType.VarChar).Value = checkcode;
        insCmd.Parameters.Add("appName", OracleType.VarChar).Value = appName;
        insCmd.Parameters.Add("IP_ADDR", OracleType.VarChar).Value = ipAddr;
        insCmd.ExecuteNonQuery();
      }
      finally {
        connMIS.Close();
      }
    }


    /// <summary>
    /// User資訊物件(Device資訊與員工資訊)
    /// </summary>
    public class UserInfo
    {
      private string _UUID;
      private string _DeviceType;
      private string _Username;
      private string _CName;
      private string _PPS_CODE;
      private string _WindowsID;
      private string _IP;

      public string UUID { get { return this._UUID; } }
      public string DeviceType { get { return this._DeviceType; } }
      public string Username { get { return this._Username; } }
      public string CName { get { return this._CName; } }
      public string PPS_CODE { get { return this._PPS_CODE; } }
      public string WindowsID { get { return this._WindowsID; } }
      public string IP { get { return this._IP; } }


      public UserInfo(DataRow row, System.Web.HttpRequest req) {
        _UUID = row["UUID"].ToString();
        _DeviceType = row["DEVICE_TYPE"].ToString();
        _Username = row["USERNAME"].ToString();
        _PPS_CODE = row["PPS_CODE"].ToString();
        _CName = OracleKitUser.getEmpName(_PPS_CODE);
        _WindowsID = row["WIN_ID"].ToString();
        _IP = ChrisKit.getIPAddress(req);
      }
    }


    #region iDevice Methods

    public static string[] iDeviceFamilys = { "iPhone", "iPad", "iPod" };
    public static Dictionary<string, string> iDeviceModelDict = new Dictionary<string, string>() {
      { "iPhone2,1", "iPhone 3GS" },
      { "iPhone3,1", "iPhone 4" },
      { "iPhone3,2", "iPhone 4" },
      { "iPhone3,3", "iPhone 4" },
      { "iPhone4,1", "iPhone 4s" },
      { "iPhone5,1", "iPhone 5" },
      { "iPhone5,2", "iPhone 5" },
      { "iPhone5,3", "iPhone 5c" },
      { "iPhone5,4", "iPhone 5c" },
      { "iPhone6,1", "iPhone 5s" },
      { "iPhone6,2", "iPhone 5s" },
      { "iPhone7,1", "iPhone 6 Plus" },
      { "iPhone7,2", "iPhone 6" },
      { "iPad1,1", "iPad 1"},
      { "iPad2,1", "iPad 2"},
      { "iPad2,2", "iPad 2"},
      { "iPad2,3", "iPad 2"},
      { "iPad2,4", "iPad 2"},
      { "iPad2,5", "iPad mini" },
      { "iPad2,6", "iPad mini" },
      { "iPad2,7", "iPad mini" },
      { "iPad3,1", "iPad 3"},
      { "iPad3,2", "iPad 3"},
      { "iPad3,3", "iPad 3"},
      { "iPad3,4", "iPad 4"},
      { "iPad3,5", "iPad 4"},
      { "iPad3,6", "iPad 4"},
      { "iPad4,1", "iPad Air"},
      { "iPad4,2", "iPad Air"},
      { "iPad4,3", "iPad Air"},
      { "iPad4,4", "iPad mini Retina"},
      { "iPad4,5", "iPad mini Retina"},
      { "iPad4,6", "iPad mini Retina"},
      { "iPod", "iPod touch" },
      { "x86_64", "iPhone Simulator" }
    };


    /// <summary>
    /// 由 UserAgent 字串抓取 iOS 版本號
    /// </summary>
    /// <param name="UserAgent">UserAgent 字串</param>
    /// <returns>iOS 版本號</returns>
    public static string GetIOSVersion(string UserAgent) {
      string pattern1 = @"(iPhone|CPU) OS [0-9]+[_\.][0-9][_\.]?[1-9]*";
      Match match1 = Regex.Match(UserAgent, pattern1);
      if (match1.Success) {
        string subPattern = @"[0-9]+[_\.][0-9][_\.]?[1-9]*";
        Match verMatch = Regex.Match(match1.Value, subPattern);
        return verMatch.Value.Replace('_', '.');
      }
      return "UNKNOWN_VER";
    }

    public static string GetiDeviceName(string UserAgent)
    {
        foreach (string deviceName in iDeviceFamilys)
        {
            if (UserAgent.Contains(deviceName + ";"))
                return deviceName;
        }
        return "Other";
    }

    /// <summary>
    /// 判斷 UserAgent 是否為 Mobile Safari
    /// </summary>
    /// <param name="UserAgent">UserAgent 字串</param>
    /// <returns>Mobile Safari為true，否則false</returns>
    public static bool isMobileSafari(string UserAgent) {
      if (UserAgent.Contains("Mobile/") && UserAgent.Contains("Safari/")) {
        foreach (string iDeviceFamily in iDeviceFamilys) {
          if (UserAgent.Contains(iDeviceFamily + ";"))
            return true;
        }
      }
      return false;
    }


    /// <summary>
    /// 判斷裝置是否為 iPad mini
    /// </summary>
    /// <param name="deviceType">DeviceType 字串</param>
    /// <returns>iPad mini為true，否則false</returns>
    public static bool isIPadMini(string deviceType) {
      string deviceModel = GetIDeviceModel(deviceType);
      if (deviceModel.StartsWith("iPad mini"))
        return true;
      else
        return false;
    }


    /// <summary>
    /// 判斷裝置是否為 iOS Device
    /// </summary>
    /// <param name="deviceType">DeviceType 字串</param>
    /// <returns>iOS Device為true，否則false</returns>
    public static bool isIOSDevice(string deviceType) {
      foreach (string iDeviceFamily in iDeviceFamilys) {
        if (deviceType.StartsWith(iDeviceFamily))
          return true;
      }
      return false;
    }


    /// <summary>
    /// 取得 iOS Device 產品 Family 名稱
    /// </summary>
    /// <param name="deviceType">DeviceType 字串</param>
    /// <returns>iOS Device 產品 Family，例如iPad</returns>
    public static string getIDeviceFamily(string deviceType) {
      if (string.IsNullOrEmpty(deviceType))
        return null;

      foreach (string iDeviceFamily in iDeviceFamilys) {
        if (deviceType.StartsWith(iDeviceFamily)) {
          if (iDeviceFamily == "iPod") {
            return "iPod touch";
          }
          else if (iDeviceFamily == "iPad") {
            if (isIPadMini(deviceType))
              return "iPad mini";
          }
          return iDeviceFamily;
        }
      }
      return deviceType;
    }


    /// <summary>
    /// 取得 iDevice 的裝置系列名稱
    /// </summary>
    /// <param name="deviceType">DeviceType 字串</param>
    /// <returns>裝置系列名稱(例如iPhone 5s)，若找不到則回傳deviceType</returns>
    public static string GetIDeviceModel(string deviceType) {
      foreach (string modelKey in iDeviceModelDict.Keys) {
        if (deviceType.StartsWith(modelKey))
          return iDeviceModelDict[modelKey];
      }
      return deviceType;
    }

    public static string GetIPAddress(System.Web.HttpRequest req)
    {
        string ipAddress = req.ServerVariables["HTTP_X_FORWARDED_FOR"];
        if (!string.IsNullOrEmpty(ipAddress))
        {
            string[] addresses = ipAddress.Split(',');
            if (addresses.Length > 0)
                return addresses[0];
        }
        return req.ServerVariables["REMOTE_ADDR"];
    }

    public static string GetAppNameVersion(string UserAgent)
    {
        const string pattern = @"^[\w \-\+]+[/ ][0-9]+\.[0-9]+\.?[0-9]* [\(C]";
        if (Regex.IsMatch(UserAgent, pattern))
        {
            string matchStr = Regex.Match(UserAgent, pattern).Value;
            return matchStr.Substring(0, matchStr.Length - 2);
        }
        return kUnknownVersionStr;
    }

    public static string GetAppVersion(string UserAgent)
    {
        string AppNameVerStr = GetAppNameVersion(UserAgent);
        int dotIndex = AppNameVerStr.IndexOf(".");
        if (dotIndex > 0)
        {
            for (int i = dotIndex - 1; i >= 0; i--)
            {
                if (!isNumber(AppNameVerStr[i]))
                    return AppNameVerStr.Substring(i + 1);
            }
            return AppNameVerStr;
        }

        return kUnknownVersionStr;
    }

    public static bool isNumber(char inChar)
    {
        int charCode = (int)inChar;
        if (charCode >= 0x30 && charCode <= 0x39)
            return true;
        else
            return false;
    }
    #endregion

  }
}